/**
 *	@brief  FactoriesServer classes responsible for keeping factories and creating objects
 *	@file	FactoriesServer.hpp	Contains collections of factories producing elements of type given as template parameter
 *	@author Szymon Janikowski
 *	@author Pawel Gilewski
 */
#ifndef FACTORIES_SERVER_H
#define FACTORIES_SERVER_H


#include "Factory.hpp"

#include <map>
#include <vector>
#include <string>
#ifdef DEBUG_F
#include <iostream>
#endif
#include <boost/thread/mutex.hpp>


namespace pluglib
{
	using namespace std;


	/**
	 * @brief Base class for all the FactoriesServer objects.
	 * @note Needed for keeping all those classes in containter
	 */
	class FactoriesServerBase
	{
	public:
		virtual void getFactoriesNames(vector<string> &names)=0;
		virtual ~FactoriesServerBase(){};
	};

	/**
	 * @brief	Class for keeping and using Factories server of certain type.
	 * @param	InterfaceType type of the objects returned from the create methods
	 *			of factories kept in server. Methods of the server are thread safe.
	 */
	template<typename InterfaceType> class FactoriesServer : public FactoriesServerBase
	{
	public:
		///	@brief	There is only one constructor
		///	@param	name unique name of the created server
		/// @note	Server always has to have a name
		FactoriesServer(string name)
		{
			name_ = name;
			#ifdef DEBUG_F
			cout << "Server named " << name << " created!"<<endl;
			#endif
		}

		~FactoriesServer(void)
		{
			for(FactoriesMapIterator it=factories_.begin(); it!=factories_.end(); ++it)
			{
				delete it->second;
			}
		}

		/**
		 *	@brief	Function responsible for adding certain factory to collection
		 *  @note	Automaticaly extracs the name which should be given during construction 
		 *			of the Factory object. The method is synchronized.
		 *	@param	factory factory to add.
		 */
		void addFactory(Factory* factory)
		{
			boost::mutex::scoped_lock(mutex);

			#ifdef DEBUG_F
				cout << "Registering factory in server " << name_ << endl;
			#endif
			
			FactoriesMapIterator factoriesMapIterator;
			factoriesMapIterator = factories_.find(factory->getName());
			if (factoriesMapIterator == factories_.end()) 
				factories_[factory->getName()]=factory;
			#ifdef DEBUG_F
			else
				cout << "Factory already in factory server " << name_ << endl;
			#endif
		}
		
		/**
		 *	@brief	Function responsible for creating instances of implementations from plugins
		 *	@note	Objects returned from create method of a Factory returns object of type InterfaceBase.
					Dynamic casting is done here. The method is synchronized.
		 *	@param	name name of the factory, that should be used for creating object
		 *	@return Desired object if operation was successful. NULL if not. 
		 */
		InterfaceType* create(const string& name)
		{
			
			boost::mutex::scoped_lock(mutex);
			FactoriesMapIterator factoriesMapIterator;
			factoriesMapIterator = factories_.find(name);
			if (factoriesMapIterator != factories_.end())
			{
				try{
					InterfaceType* result = dynamic_cast<InterfaceType*>(factories_.find(name)->second->create());
					return result;
				}catch(...)
				{
					#ifdef DEBUG_F
						cout<<"Exception during casting to interface type"<<endl;
					#endif
					return NULL;
				}			
			}
			else
			{
				#ifdef DEBUG_F
					cout<<"Name "<< name <<" of the factory not found"<<endl;
				#endif
				return NULL;	
			}
		}

		/**
		 *	@brief	Counts the exsisting factories
		 *	@note	The method is synchronized
		 *	@return	Size of factories_ vector.
		 */
		int factoriesCount()
		{
			boost::mutex::scoped_lock(mutex);
			return factories_.size();
		}

		/**
		 *	@brief	Returns the FactoriesServer name
		 *	@return	name of the factory
		 */
		string getName()
		{
			return name_;
		}

		/**
		 *	@brief	Convenience function, that fill the vector by factory names
		 *	@param	names Reference to vector, where the found names will be put
		 */
		void getFactoriesNames(vector<string> &names)
		{
			boost::mutex::scoped_lock(mutex);
			if (!factories_.empty())
			{
				FactoriesMapIterator fmit = factories_.begin();
				names.clear();
				for(fmit = factories_.begin(); fmit != factories_.end(); ++fmit)
					/*if (fmit != factories_.end())*/ names.push_back(fmit->first);
				#ifdef DEBUG_F
					cout << names.size() << " names obtained" << endl;
				#endif
			}		
			#ifdef DEBUG_F
			else
				cout << endl << " Factories server empty" << endl;
			#endif
		}
	private:
		
		// mutex semaphore - needed to synchronization
		boost::mutex mutex;

		typedef map<string,Factory*>			FactoriesMap;
		typedef FactoriesMap::iterator			FactoriesMapIterator;

		FactoriesMap factories_;
		string name_;
	};//template class FactoriesServer

//-----------------------------------------------------------------------------------------------------------
	/*
	/// @brief Convenience function for adding new factory without specyfing server name
	/// Automated server casting is done.
	/// @note Factory has a field with the name of the server, to which it should belong to. It is used here.
	template <typename InterfaceType>
	void addFactory(Factory* newFactory)
	{
		
		try
		{
			FactoriesServer<InterfaceType>* factoriesServer = 
			dynamic_cast<FactoriesServer<InterfaceType>*> (Engine::getInstance().getServer(newFactory->getServerName()));
			factoriesServer->addFactory(newFactory);
		}
		catch(...)
		{
			#ifdef DEBUG_F
					cout<<"Exception during casting to server"<<endl;
			#endif
			return;
		}
		
	};//function addFactory
	*/

};//namespace pluglib
#endif
